//
//  TransmissionBroker.cpp
//  Simulation
//
//  Created by Christopher Kemsley on 3/5/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include "TransmissionBroker.hpp"
#include <cmath>
#include <cstring>
#include "SimulationParameters.hpp"
#include "Log.hpp"


using namespace Sim ;
using Statistics::Node ;







TransmitterProfile :: TransmitterProfile ( Location const & l , Distance const & r , Channel const & c )
:	ReceiverProfile ( l , c )
,	txRange ( r )
	{
	
	}
TransmitterProfile :: TransmitterProfile ( TransmitterProfile const & copy )
:	ReceiverProfile ( copy )
,	txRange ( copy.txRange )
	{
	
	}
TransmitterProfile :: ~TransmitterProfile ()
	{
	
	}
void	TransmitterProfile :: operator=	( TransmitterProfile const & copy )
	{
	ReceiverProfile::operator= ( copy ) ;
	txRange = copy.txRange ;
	}





ReceiverProfile :: ReceiverProfile ( Location const & l , Channel const & c )
:	location ( l )
,	channel ( c )
	{
	
	}
ReceiverProfile :: ReceiverProfile ( ReceiverProfile const & copy )
:	location ( copy.location )
,	channel ( copy.channel )
	{
	
	}
ReceiverProfile :: ~ReceiverProfile ()
	{
	
	}


bool	ReceiverProfile :: receives		( TransmitterProfile const & profile ) const
	{
	if (!( profile.channel == channel ))
		return false ;
	double dX	=	profile.location.x - location.x ;
	double dY	=	profile.location.y - location.y ;
	double d	=	::sqrt ( (dX*dX) + (dY*dY) ) ;
	return d <= profile.txRange ;
	}
void	ReceiverProfile :: operator=	( ReceiverProfile const & copy )
	{
	location = copy.location ;
	channel = copy.channel ;
	}









Message*	Message :: create	( TransmitterProfile const & profile , DeviceID sender , DeviceID target , Byte const * data , unsigned length )
	{
	return new Message ( profile , sender , target , data , length ) ;
	}


Message :: Message ( TransmitterProfile const & p , DeviceID s , DeviceID t , Byte const * d , unsigned l )
:	sender ( s )
,	target ( t )
,	bytes ( new Byte [l] )
,	length ( l )
,	transmitter ( p )
	{
	memcpy ( (void*)bytes , (void const*)d , (size_t)length ) ;
	}
Message :: ~Message ()
	{
	delete [] bytes ;
	}







Transmitter :: Transmitter	( DeviceID const & identifier , TransmitterDelegate & delegate )
:	mIdentifier(identifier)
,	mDelegate(delegate)
,	mCurrent(0)
,	mCompletionTimer(0)
,	mStats ( std::string("Transmitter (")+identifier+")" )
	{
	
	}
Transmitter :: ~Transmitter ()
	{
	if ( mCurrent != 0 )
		cancel() ;
	mCompletionTimer << 0 ;
	}

bool	Transmitter :: isTransmitting () const
	{
	return mCurrent != 0 ;
	}
void	Transmitter :: transmit	( Byte const * data , Length length , DeviceID target , TransmitterProfile const & profile )
	{
	if ( mCurrent != 0 )
		ThrowException ( "Transmitter" , "transmit called, but currently transmitting" , __FILE__,__LINE__ ) ;
	
	mStats << Node::kBusy ;
	
	Log::states("Transmitter") << "Beginning transmission to " << target ;
	
	// Create the message
	mCurrent = Message :: create ( profile , mIdentifier , target , data , length ) ;
	TransmissionBroker::shared() . addMessage ( mCurrent.pointer() ) ;
	
	// Create the timer
	Invocation * i = ObjectInvocation<Transmitter> :: invocation ( this , &Transmitter::_callback ) ;
	Interval duration = SimulationParameters::shared() . activityDuration ( length ) ;
	mCompletionTimer << new Timer ( duration , i ) ;
	}
void	Transmitter :: cancel		()
	{
	if ( mCurrent == 0 )
		ThrowException ( "Transmitter" , "cancel, but not currently transmitting" , __FILE__,__LINE__ ) ;
	
	mStats << ( Node::kIdle | Node::kRetroactive ) ;
	mStats << Node::kIdle ;
	
	Log::states("Transmitter") << "Cancelled transmitting" ;
	
	TransmissionBroker::shared() . removeMessage ( mCurrent.pointer() ) ;
	mCurrent = 0 ;
	mCompletionTimer << 0 ;
	}
void	Transmitter :: _callback ()
	{
	if ( mCurrent == 0 )
		ThrowException ( "Transmitter" , "callback called, but not currently transmitting" , __FILE__,__LINE__ ) ;
	
	mStats << Node::kIdle ;
	
	Log::states("Transmitter") << "Done transmitting" ;
	
	TransmissionBroker::shared() . removeMessage ( mCurrent.pointer() ) ;
	mCurrent = 0 ;
	mCompletionTimer << 0 ;
	mDelegate . transmissionDidComplete() ;
	}





Receiver :: Receiver ( DeviceID const & identifier , ReceiverProfile const & profile , ReceiverDelegate & delegate )
:	mDelegate(delegate)
,	mProfile(profile)
,	mMessage(0)
,	mAllMessages()
,	mStats ( std::string("Receiver (") + identifier + ")" )
,	mCallbackState(kCallbackEnabled)
	{
	TransmissionBroker::shared() . addReceiver ( this ) ;
	}
Receiver :: ~Receiver ()
	{
	TransmissionBroker::shared() . removeReceiver ( this ) ;
	}

bool	Receiver :: isIdle	() const
	{
	return mAllMessages.size() == 0 ;
	}
void	Receiver :: enable	()
	{
	if ( isIdle() )
		mCallbackState = kCallbackEnabled ;
	else
		mCallbackState = kCallbackIgnore ;
	}
void	Receiver :: disable	()
	{
	mCallbackState = kCallbackNever ;
	}
void	Receiver :: setProfile ( ReceiverProfile const & profile )
	{
	// Remove us from the pool
	TransmissionBroker::shared() . removeReceiver ( this ) ;
	
	// Clean up
	mAllMessages.clear() ;
	if ( mMessage != 0 )
		{
		mMessage = 0 ;
		}
	
	// Save new info
	mProfile = profile ;
	
	// Add us to the pool
	TransmissionBroker::shared() . addReceiver ( this ) ;
	
	// If we immediately got a TX, mark it as bad
	if ( mMessage != 0 )
		{
		mMessage = 0 ;
		}
	}


void	Receiver :: beginReceipt	( Message * message )
	{
	if ( ! mProfile.receives(message->transmitter) )
		return ; // we don't like it. And we know it.
	if ( mAllMessages.find(message) != mAllMessages.end() )
		ThrowException ( "Receiver" , "beginReceipt called, but already started receiving "<<message , __FILE__,__LINE__ ) ;
	
	switch ( mAllMessages.size() )
		{
			case 0 : {
				Log::states("Receiver") << "beginReceipt("<<this<<","<<message<<") - first transmission" ;
				mMessage = message ;
				if ( mCallbackState == kCallbackEnabled )
					mDelegate . receiveDidBegin() ;
				
				mStats << Node::kBusy ;
			} break ;
			
			case 1 : {
				if ( message != 0 )
					{
					Log::states("Receiver") << "beginReceipt("<<this<<","<<message<<") - second transmission. Garbled. Ugh." ;
					mMessage = 0 ;
					
					mStats << ( Node::kErronious | Node::kRetroactive ) ;
					}
				else
					{
					Log::states("Receiver") << "beginReceipt("<<this<<","<<message<<") - dropped back to two, but still garbled" ;
					}
			} break ;
			
			default : break ;
		}
	
	mAllMessages . insert ( message ) ;
	}
void	Receiver :: cancelReceipt	( Message * message )
	{
	if ( mAllMessages.find(message) == mAllMessages.end() )
		return ; // we're not detecting it anyways. pwned!
	if ( mAllMessages.find(message) == mAllMessages.end() )
		ThrowException ( "Receiver" , "cancelReceipt called, but not currently receiving" , __FILE__,__LINE__ ) ;
	
	switch ( mAllMessages.size() )
	{
		case 0 : {
			ThrowException ( "Receiver" , "cancelReceipt("<<this<<","<<message<<") called, but we have no messages" , __FILE__,__LINE__ ) ;
		} break ;
		
		case 1 : {
			Log::states("Receiver") << "cancelReceipt("<<this<<","<<message<<") - last received rx done (but cancelled)" ;
			if ( mCallbackState == kCallbackEnabled )
				{
				mDelegate . receiveDidFail() ;
				}
			else if ( mCallbackState == kCallbackIgnore )
				{
				mCallbackState = kCallbackEnabled ;
				}
			
			mMessage = 0 ;
			mStats << Node::kIdle ;
		} break ;
		
		default : break ;
	}
	
	mAllMessages.erase(message) ;
	}
void	Receiver :: endReceipt		( Message * message )
	{
	if ( mAllMessages.find(message) == mAllMessages.end() )
		return ; // we're not detecting it anyways. pwned!
	if ( mAllMessages.find(message) == mAllMessages.end() )
		ThrowException ( "Receiver" , "endReceipt called, but not currently receiving" , __FILE__,__LINE__ ) ;
	
	switch ( mAllMessages.size() )
		{
			case 0 : {
				ThrowException ( "Receiver" , "endReceipt("<<this<<","<<message<<") called, but we have no messages" , __FILE__,__LINE__ ) ;
			} break ;
			
			case 1 : {
				Log::states("Receiver") << "endReceipt("<<this<<","<<message<<") - last received rx done" ;
				if ( mCallbackState == kCallbackEnabled )
					{
					if ( mMessage != 0 )
						mDelegate . receiveDidComplete ( mMessage.pointer() ) ;
					else
						mDelegate . receiveDidFail() ;
					}
				else if ( mCallbackState == kCallbackIgnore )
					{
					mCallbackState = kCallbackEnabled ;
					}
				
				mMessage = 0 ;
				mStats << Node::kIdle ;
			} break ;
			
			default : break ;
		}
	
	mAllMessages.erase(message) ;
	}






TransmissionBroker :: TransmissionBroker ()
:	receivers() , messages() , pendingAdditions() , pendingRemovals() , pendingCancellations() { }
TransmissionBroker :: ~TransmissionBroker () { }

TransmissionBroker&	TransmissionBroker :: shared ()
	{
	static TransmissionBroker s ;
	return s ;
	}

void	TransmissionBroker :: addReceiver		( Receiver * sender )
	{
	receivers . insert ( sender ) ;
	
	std::set<OP<Message> >::iterator	here ;
	std::set<OP<Message> >::iterator	end ;
	
	
	/** Process additions **/
	end = messages.end() ;
	for ( here=messages.begin() ; here!=end ; here++ )
		{
		OP<Message> const & pMessage	=	*here ;
		
		messages . insert ( pMessage ) ;
		
		sender -> beginReceipt ( pMessage.pointer() ) ;
		}
	}
void	TransmissionBroker :: removeReceiver	( Receiver * sender )
	{
	receivers . erase ( sender ) ;
	}

void	TransmissionBroker :: addMessage		( Message * message )
	{
	if ( pendingAdditions.find(message) != pendingAdditions.end() )
		ThrowException ( "TransmissionBroker" , "Added message, but already pending addition" , __FILE__,__LINE__ ) ;
	if ( pendingRemovals.find(message) != pendingRemovals.end() )
		ThrowException ( "TransmissionBroker" , "Added message, but already pending removal" , __FILE__,__LINE__ ) ;
	if ( messages.find(message) != messages.end() )
		ThrowException ( "TransmissionBroker" , "Added message, but already in messages" , __FILE__,__LINE__ ) ;
	pendingAdditions . insert ( message ) ;
	}
void	TransmissionBroker :: removeMessage		( Message * message )
	{
	if ( pendingAdditions.find(message) != pendingAdditions.end() )
		ThrowException ( "TransmissionBroker" , "Removed message, but not yet in circulation" , __FILE__,__LINE__ ) ;
	if ( pendingRemovals.find(message) != pendingRemovals.end() )
		ThrowException ( "TransmissionBroker" , "Removed message, but already pending removal" , __FILE__,__LINE__ ) ;
	if ( messages.find(message) == messages.end() )
		ThrowException ( "TransmissionBroker" , "Removed message, but not currently in messages" , __FILE__,__LINE__ ) ;
	pendingRemovals . insert ( message ) ;
	}
void	TransmissionBroker :: cancelMessage		( Message * message )
	{
	if ( pendingAdditions.find(message) != pendingAdditions.end() )
		ThrowException ( "TransmissionBroker" , "Cancelled message, but not yet in circulation" , __FILE__,__LINE__ ) ;
	if ( pendingRemovals.find(message) != pendingRemovals.end() )
		ThrowException ( "TransmissionBroker" , "Cancelled message, but already pending removal" , __FILE__,__LINE__ ) ;
	if ( messages.find(message) == messages.end() )
		ThrowException ( "TransmissionBroker" , "Cancelled message, but not currently in messages" , __FILE__,__LINE__ ) ;
	pendingCancellations . insert ( message ) ;
	}

Interval const	TransmissionBroker :: minimumTimeInterval () const
	{
	if ( pendingRemovals.size()!=0 || pendingAdditions.size()!=0 )
		return Interval::zeroTime ;
	return Interval::distantFuture ;
	}
void	TransmissionBroker :: systemDidAdvance	( Interval const & from , Interval const & to , bool isZeroTime )
	{
	std::set<OP<Message> >::iterator	here ;
	std::set<OP<Message> >::iterator	end ;
	
	std::set<Receiver*>::iterator	h ;
	std::set<Receiver*>::iterator	e ;
	
	
	/** Process removals **/
	end = pendingRemovals.end() ;
	for ( here=pendingRemovals.begin() ; here!=end ; here++ )
		{
		OP<Message> const & pMessage	=	*here ;
		
		messages . erase ( pMessage ) ;
		
		e = receivers.end() ;
		for ( h=receivers.begin() ; h!=e ; h++ )
			{
			Receiver * const & pReceiver = *h ;
			pReceiver -> endReceipt ( pMessage.pointer() ) ;
			}
		}
	
	
	/** Process additions **/
	end = pendingAdditions.end() ;
	for ( here=pendingAdditions.begin() ; here!=end ; here++ )
		{
		OP<Message> const & pMessage	=	*here ;
		
		messages . insert ( pMessage ) ;
		
		e = receivers.end() ;
		for ( h=receivers.begin() ; h!=e ; h++ )
			{
			Receiver * const & pReceiver = *h ;
			pReceiver -> beginReceipt ( pMessage.pointer() ) ;
			}
		}
	
	
	/** Process additions **/
	end = pendingCancellations.end() ;
	for ( here=pendingCancellations.begin() ; here!=end ; here++ )
		{
		OP<Message> const & pMessage	=	*here ;
		
		messages . insert ( pMessage ) ;
		
		e = receivers.end() ;
		for ( h=receivers.begin() ; h!=e ; h++ )
			{
			Receiver * const & pReceiver = *h ;
			pReceiver -> cancelReceipt ( pMessage.pointer() ) ;
			}
		}
	
	pendingAdditions.clear() ;
	pendingRemovals.clear() ;
	}






